#include "doorbell_ota.h"
#include "esp_https_ota.h"
#include "esp_ota_ops.h"
#include "doorbell_mqtt.h"
#include "esp_crt_bundle.h"
#include <esp_log.h>

#define TAG "doorbell_ota"

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
        break;
    case HTTP_EVENT_ON_DATA:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
        break;
    case HTTP_EVENT_REDIRECT:
        ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
        break;
    }
    return ESP_OK;
}

static void doorbell_ota_process(void *arg)
{
    ESP_LOGI(TAG, "Starting OTA");
    esp_http_client_config_t config = {
        .url = FIRMWARE_URL,
        .crt_bundle_attach = esp_crt_bundle_attach,
        .event_handler = _http_event_handler,
        .keep_alive_enable = true,
        .skip_cert_common_name_check = true,
    };

    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };
    ESP_LOGI(TAG, "Attempting to download update from %s", config.url);
    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK)
    {
        ESP_LOGI(TAG, "OTA Succeed, Rebooting...");
        doorbell_mqtt_publish("{\"status\":\"upgraded\"}", 21);
        vTaskDelay(pdMS_TO_TICKS(1000));
        esp_restart();
    }
    else
    {
        doorbell_mqtt_publish("{\"status\":\"upgrade_failed\"}", 27);
        ESP_LOGE(TAG, "Firmware upgrade failed");
    }
}

static void doorbell_ota_version(void *arg)
{
    doorbell_mqtt_publish("{\"version\":\"" MAJOR "." MINOR "." PATCH "\"}", 19);
}

void doorbell_ota_init()
{
    // 1. 注册mqtt命令回调
    doorbell_mqtt_cmd_cb cmd_cb = {
        .cmd = "ota",
        .cb = doorbell_ota_process,
        .arg = NULL,
    };
    doorbell_mqtt_register_callback(&cmd_cb);

    // 2. 注册查询版本号的命令
    doorbell_mqtt_cmd_cb version_cb = {
        .cmd = "version",
        .cb = doorbell_ota_version,
        .arg = NULL,
    };
    doorbell_mqtt_register_callback(&version_cb);
}

void doorbell_ota_confirm()
{
    // 如果固件自检完成，需要调用固件确认函数, 首先尝试获取分区当前状态
    esp_ota_img_states_t state;
    esp_err_t ret = esp_ota_get_state_partition(esp_ota_get_running_partition(), &state);

    // 如果目前我们是factory分区，那么无法获取状态
    if (ret == ESP_OK && state == ESP_OTA_IMG_PENDING_VERIFY)
    {
        ESP_ERROR_CHECK(esp_ota_mark_app_valid_cancel_rollback());
    }
}
